package org.ellisande.tld;

/*
 * Copyright 2001-2005 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.text.WordUtils;
import org.ellisande.tld.annotations.Description;
import org.ellisande.tld.annotations.EvaluateExpressions;
import org.ellisande.tld.annotations.Info;
import org.ellisande.tld.annotations.Required;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.AnnotationTypeFilter;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.StaxDriver;

/**
 * Goal which generates a tld
 * 
 * @goal generate
 * 
 * @phase install
 * 
 * @requiresProject yes
 * 
 * @requiresDependencyResolution compile
 * 
 * @requiresDependencyCollection yes
 * 
 */
public class TldGen {

	/**
	 * Location of the file.
	 * 
	 * @parameter expression="${tldgen.outputDirectory}"
	 * @required
	 */
	private File outputDirectory;

	/**
	 * Base package to be scanner for tag annotations
	 * 
	 * @parameter expression="${tldgen.basePackage}"
	 * @required
	 */
	private String basePackage;

	/**
	 * Name for the taglib to be generated
	 * 
	 * @parameter expression="${tldgen.taglibName}"
	 * @required
	 */
	private String taglibName;

	public static void main(String[] args) throws Exception {
		TldGen mojo = new TldGen();
		mojo.execute();
	}

	public void execute() {
		taglibName="grid_tags";
		basePackage="org.ellisande.grid";
		outputDirectory= new File("src/main/resources/META-INF/");
		ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
		scanner.addIncludeFilter(new AnnotationTypeFilter(org.ellisande.tld.annotations.Tag.class));
		List<Class<?>> tags = new ArrayList<Class<?>>();
		for (BeanDefinition bd : scanner.findCandidateComponents(basePackage)) {
			Class<?> tagClass = null;
			try {
				tagClass = Class.forName(bd.getBeanClassName());
			} catch (ClassNotFoundException e) {
				throw new RuntimeException("Class not found.");
			}
			tags.add(tagClass);
		}

		XStream xstream = new XStream(new StaxDriver());
		xstream.autodetectAnnotations(true);

		Taglib taglib = new Taglib(parseTags(tags));
		String xml = xstream.toXML(taglib);

		File f = outputDirectory;

		if (!f.exists()) {
			f.mkdirs();
		}

		File tld = new File(f, taglibName + ".tld");

		FileWriter writer = null;
		try {
			writer = new FileWriter(tld);

			writer.write(xml);
		} catch (IOException e) {
			throw new RuntimeException("Error creating file " + tld, e);
		} finally {
			if (writer != null) {
				try {
					writer.close();
				} catch (IOException e) {
					// ignore
				}
			}
		}
	}

	private List<Tag> parseTags(List<Class<?>> tagClasses) {
		final List<Tag> tags = new ArrayList<Tag>();
		for (Class<?> clazz : tagClasses) {
			final Tag tag = new Tag();
			String tagName = clazz.getSimpleName();
			tagName = tagName.replace("Tag", "");
			tagName = WordUtils.uncapitalize(tagName);

			tag.setName(tagName);
			tag.setTagClass(clazz);
			tag.setBodyContent(clazz.getAnnotation(org.ellisande.tld.annotations.Tag.class).bodyContent().toString());
			if (clazz.isAnnotationPresent(Info.class)) {
				tag.setInfo(clazz.getAnnotation(Info.class).value());
			}

			List<Attribute> attributes = new ArrayList<Attribute>();
			getAttributes(clazz, attributes);
			tag.setAttributes(attributes);
			tags.add(tag);
		}
		return tags;
	}

	private void getAttributes(Class<?> clazz, List<Attribute> attributes) {
		if (clazz.equals(Object.class)) {
			return;
		}
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields) {
			if (field.isAnnotationPresent(org.ellisande.tld.annotations.Attribute.class)) {
				final Attribute attribute = new Attribute();
				attribute.setName(field.getName());

				if (field.isAnnotationPresent(Description.class)) {
					Description description = field.getAnnotation(Description.class);
					attribute.setDescription(description.value());
				}
				if (field.isAnnotationPresent(EvaluateExpressions.class)) {
					attribute.setEvaluateExpressions(true);
				}
				if (field.isAnnotationPresent(Required.class)) {
					attribute.setRequired(true);
				}

				attributes.add(attribute);
			}
		}

		getAttributes(clazz.getSuperclass(), attributes);
	}
}
